1 module targets.psvita;
2 import std.exception;
3 import commons;
4 
5 private enum updateCmd = "sudo apt-get update";
6 private enum depsInstallCmd = "sudo apt-get install make git-core cmake python curl wget";
7 private enum vdpmRepo = "https://github.com/vitasdk/vdpm";
8 private enum bootstrapVsdk = "./bootstrap-vitasdk.sh";
9 private enum installAllVsdk = "./install-all.sh";
10 private enum vitasdkExports =
11     "\n"~"export VITASDK=/usr/local/vitasdk" ~
12     "\n"~"export PATH=$VITASDK/bin:$PATH #adds vitasdk tool to $PATH";
13 
14 private enum vdpmInstallCmd =
15     "git clone https://github.com/vitasdk/vdpm && "~
16     "cd vdpm && "~
17     "./bootstrap-vitasdk-.sh "~
18     "./install-all.sh";
19 
20 
21 private bool setupPsvitaLinux(ref Terminal t, ref RealTimeConsoleInput input)
22 {
23     std.file.mkdirRecurse("/usr/local/vitasdk");
24     std.file.append(buildPath(environment["HOME"], ".bashrc"), vitasdkExports);
25     wait(spawnShell(updateCmd));
26     wait(spawnShell(depsInstallCmd));
27     wait(spawnShell(vdpmInstallCmd));
28     wait(spawnShell("vitasdk-update"));
29     return true;
30 }
31 private string getWslSource()
32 {
33     return executeShell("wsl echo -n $(wslpath \"%USERPROFILE%\")/.bashrc").output;
34 }
35 
36 private auto getExecFunc()
37 {
38     return (scope string arg)
39     {
40         version(Windows){return wait(spawnShell("wsl source "~getWslSource~" ^&^& "~arg));}
41         else return wait(spawnShell(arg));
42     };
43 }
44 
45 /** 
46  * The first build will simply generate the VPK, by mapping assets, compiling
47  * all the required stuff for PSVita, and also including the main binary (eboot.bin)
48  * after the first build is done, one must manually install the vita_sample.vpk 
49  * by using the vitashell utility.
50  * Params:
51  *   t = 
52  * Returns: 
53  */
54 private bool firstBuild(ref Terminal t)
55 {
56     string cwd = std.file.getcwd();
57     std.file.chdir(getHipPath("build", "vita", "hipreme_engine"));
58     scope(exit) std.file.chdir(cwd);
59     auto exec = getExecFunc();
60     if(exec("make") != 0)
61     {
62         t.writelnError("Make failed.");
63         return false;
64     }
65     if(exec("curl ftp://"~configs["psvIp"].str~":1337/ux0:/ -T ./vita_sample.vpk") != 0)
66     {
67         t.writelnError("Could not send the VPK.");
68         return false;
69     }
70     return true;
71 }
72 
73 /** 
74  * Instead of building the entire VPK for PS Vita, it only changes the binary, after that,
75  * it directly sends this new binary to the Package folder, so, there will be no need
76  * to extract a VPK by going into the Install Process again, making it a lot faster
77  * to both build and test.
78  *
79  *  For even faster installation, it is recomended to run a background FTP on PSV
80  * Params:
81  *   t = 
82  * Returns: 
83  */
84 private bool fastBuild(ref Terminal t)
85 {
86     enum APP_ID = "VSDK00007";
87     auto exec = getExecFunc();
88 
89     string cwd = std.file.getcwd();
90     std.file.chdir(getHipPath("build", "vita", "hipreme_engine"));
91 
92     version(Windows) enum pipe = "^|";
93     else enum pipe = "|";
94 
95     scope(exit) std.file.chdir(cwd);
96     exec("make clean");
97     if(exec("make eboot.bin") != 0)
98     {
99         t.writelnError("Could not rebuild.");
100         return false;
101     }
102     exec("echo screen on "~pipe~" nc "~configs["psvIp"].str~" "~configs["psvCmdPort"].str);
103     if(exec("curl ftp://"~configs["psvIp"].str~":1337/ux0:/app/"~APP_ID~"/ -T ./eboot.bin") != 0)
104     {
105         t.writelnError("Could not send eboot.bin");
106         return false;
107     }
108     exec("echo launch "~APP_ID~" "~pipe~" nc "~configs["psvIp"].str~" "~configs["psvCmdPort"].str);
109     return true;
110 }
111 
112 private bool setupPsvitaWindows(ref Terminal t, ref RealTimeConsoleInput input)
113 {
114     if(executeShell("where wsl").status != 0)
115     {
116         t.writelnError("Please, run a command prompt with administrator access and run `wsl --install` before developing for PSV on Windows.");
117         return false;
118     }
119     string cwd = std.file.getcwd();
120     std.file.mkdirRecurse(buildPath(cwd, "PSVita", "vitasdk"));
121 
122     string bashRc = buildPath(environment["USERPROFILE"], ".bashrc");
123     string fileToSource = getWslSource();
124     if(std.file.exists(bashRc))
125     {
126         if(executeShell("wsl source"~fileToSource~" ^&^& export -p ^| grep VITASDK").status != 0)
127             std.file.append(bashRc, vitasdkExports);
128     }
129     else std.file.append(bashRc, vitasdkExports);
130 
131     auto wslExec = (scope string[] commands...)
132     {
133         import std.array:join;
134         t.writelnHighlighted("WSL Execution: "~commands);
135         return wait(spawnShell("wsl source "~fileToSource~" ^&^& "~join(commands, " ")));
136     };
137     
138     if(wslExec(updateCmd) != 0)
139     {
140         t.writelnError("Could not update system repositores");
141         return false;
142     }
143     if(wslExec(depsInstallCmd) != 0)
144     {
145         t.writelnError("Could not setup vita dependencies");
146         return false;
147     }
148     std.file.chdir(buildPath(cwd, "PSVita"));
149     if(!std.file.exists("vdpm"))
150     {
151         if(wslExec("git clone "~vdpmRepo) != 0)
152         {
153             t.writelnError("Could not clone vdpm");
154             return false;
155         }
156     }
157     std.file.chdir(buildPath(cwd, "PSVita", "vdpm"));
158     if(wslExec("which vitasdk-update") != 0 && wslExec(bootstrapVsdk) != 0)
159     {
160         t.writelnError("Could not execute "~bootstrapVsdk);
161         wslExec("sudo rm -rf /usr/local/vitasdk");
162         return false;
163     }
164     if(wslExec(installAllVsdk) != 0)
165     {
166         t.writelnError("Could not execute "~installAllVsdk);
167         return false;
168     }
169     std.file.chdir(cwd);
170     if(wslExec("vitasdk-update") != 0)
171     {
172         t.writelnError("Could not update vitasdk");
173         return false;
174     }
175     configs["firstPsvConfig"] = true;
176     updateConfigFile();
177     return true;
178 }
179 
180 bool setupPsvita(ref Terminal t, ref RealTimeConsoleInput input)
181 {
182      if(!extractToFolder(
183         getHipPath("build", "vita", "hipreme_engine", "hipreme_engine_vita_dev_files.7z"),
184         getHipPath("build", "vita", "hipreme_engine"),
185         t, input
186     ))
187     {
188         t.writelnError("PSVita requires 7zip to extract the development files.");
189         return false;
190     }
191     //https://vitasdk.org/
192     version(Windows) return setupPsvitaWindows(t, input);
193     else version(linux) return setupPsvitaLinux(t, input);
194     else assert(false, "Not supported");
195 }
196 
197 ChoiceResult preparePSVita(Choice* c, ref Terminal t, ref RealTimeConsoleInput input, in CompilationOptions cOpts)
198 {
199     if(!("firstPsvConfig" in configs) || !configs["firstPsvConfig"].boolean)
200     {
201         if(!setupPsvita(t, input))
202             return ChoiceResult.Error;
203     }
204 	cached(() => timed(() => loadSubmodules(t, input)));    
205 	cached(() => timed(() => outputTemplateForTarget(t)));    
206 	runEngineDScript(t, "releasegame.d", configs["gamePath"].str);
207     putResourcesIn(t, getHipPath("build", "vita", "hipreme_engine", "assets"));
208 
209     string dflags = "-I="~configs["hipremeEnginePath"].str~"/modules/d_std/source "~
210     "-I="~configs["hipremeEnginePath"].str~"/dependencies/runtime/druntime/arsd-webassembly "~
211     "-d-version=PSVita " ~
212     "-d-version=PSV " ~
213     "-mtriple=armv7a-unknown-unknown " ~
214     "--revert=dtorfields "~
215     "-mcpu=cortex-a9 "~
216     "-O0 " ~
217     "-g "~
218     "-float-abi=hard "~
219     "--relocation-model=static "~
220     "-d-version=CarelessAlocation "~
221     "-d-version=ArsdUseCustomRuntime ";
222 
223     environment["DFLAGS"] = dflags;
224 
225     
226     requireConfiguration("psvIp", "Set up PSVita IP for installing your application via FTP.", t, input);
227     requireConfiguration("psvCmdPort", "Set up PSVita Command Port for automatic execution after compilation+installation.", t, input);
228 
229 
230     std.file.chdir(configs["hipremeEnginePath"].str);
231 
232     if(waitDubTarget(t, "psvita", DubArguments("build --parallel --deep --compiler=ldc2 --arch=armv7a-unknown-unknown ")) != 0)
233     {
234         t.writelnError("Could not build for PSVita.");
235         return ChoiceResult.Error;
236     }
237     environment["DFLAGS"] = "";    
238     runEngineDScript(t, "copylinkerfiles.d", 
239         "\"--compiler=ldc2 --arch=armv7a-unknown-unknown " ~
240         "--recipe="~buildPath(getBuildTarget("psvita"), "dub.json")~'"', 
241         getHipPath("build", "vita", "hipreme_engine", "libs"), 
242     dflags);
243 
244     static bool isFirstBuild = true;
245     if(isFirstBuild)
246     {
247         if(!firstBuild(t))
248         {
249             t.writelnError("Could not build PSVita vita_sample.vpk");
250             return ChoiceResult.Error;
251         }
252         isFirstBuild = false;
253     }
254     else
255     {
256         if(!fastBuild(t))
257         {
258             t.writelnError("Could not do subsequent builds.");
259             return ChoiceResult.Error;
260         }
261     }
262     
263     return ChoiceResult.None;
264 }